package com.ssy.lingxi.message.jpush;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.TypeReference;
import cn.hutool.core.util.ArrayUtil;
import cn.jiguang.common.ClientConfig;
import cn.jiguang.common.resp.APIConnectionException;
import cn.jiguang.common.resp.APIRequestException;
import cn.jiguang.common.resp.DefaultResult;
import cn.jpush.api.JPushClient;
import cn.jpush.api.push.PushResult;
import cn.jpush.api.push.model.Platform;
import cn.jpush.api.push.model.PushPayload;
import cn.jpush.api.push.model.audience.Audience;
import cn.jpush.api.push.model.notification.AndroidNotification;
import cn.jpush.api.push.model.notification.IosNotification;
import cn.jpush.api.push.model.notification.Notification;
import com.ssy.lingxi.common.exception.BusinessException;
import com.ssy.lingxi.common.response.ResponseCode;
import com.ssy.lingxi.message.websocket.util.JsonUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.math.BigDecimal;
import java.util.*;

/**
 * 推销消息服务类
 * 文档: https://docs.jiguang.cn/jpush/server/push/server_overview/
 * @author yzc
 * @version 2.0.0
 * @date 2021/3/26
 */
@Slf4j
@Service
public class JPushService {

    @Value("${jpush.appKey}")
    private String appKey;
    @Value("${jpush.masterSecret}")
    private String masterSecret;
    @Value("${jpush.apnsProduction}")
    private Boolean apnsProduction;

    private static JPushClient pushClient;
    private static final int maxPushSize = 1000;
    /**
     * 别名最大绑定设备数
     */
    private static final int MAX_ALIAS_BIND_DEVICE = 10;

    @PostConstruct
    public void test() {
        ClientConfig cfg = ClientConfig.getInstance();
        // true为生产环境, false为测试环境
        cfg.setApnsProduction(apnsProduction);

        pushClient = new JPushClient(masterSecret, appKey);
    }

    /**
     * 推送消息
     *
     * @param msg   通知内容
     * @param alias 接受消息人的别名
     * @param extras 自定义内容
     * @return
     */
    public void pushMessage(String msg, List<String> alias, Object extras) {
        // 自定义内容
        Map<String, String> extrasMap = Convert.convert(new TypeReference<Map<String, String>>() {
        }, extras);

        if (CollUtil.isEmpty(alias)) {
            log.error("推送别名list不能为空");
            throw new BusinessException(ResponseCode.BUSINESS_ERROR);
        }
        // 极光推送单次上限为1000
        if (alias.size() <= maxPushSize) {
            log.info("推送接收别名, alias:{}", JsonUtil.toJsonStr(alias));
            PushPayload payload = buildAliasPush(msg, ArrayUtil.toArray(alias, String.class), extrasMap);
            this.tryPush(payload);
        } else {
            // 推送次数,向上取整
            BigDecimal time = new BigDecimal(alias.size()).divide(BigDecimal.valueOf(maxPushSize), 0, BigDecimal.ROUND_UP);
            int pushTime = time.intValue();
            // 分批推送
            for (int i = 1; i <= pushTime; i++) {
                List<String> aliasList = CollUtil.page(i, maxPushSize, alias);
                log.info("分批推送第" + i + "次推送，推送接收别名, alias:{}", JsonUtil.toJsonStr(aliasList));
                PushPayload payload = buildAliasPush(msg, ArrayUtil.toArray(aliasList, String.class), extrasMap);
                this.tryPush(payload);
            }
        }
    }

    /**
     * TODO 调用失败重试
     * 发送推送
     * @param payload
     */
    private void tryPush(PushPayload payload) {
        PushResult result = null;
        try {
            result = pushClient.sendPush(payload);
            log.info("极光推送返回结果, result:{}", result);
        } catch (APIConnectionException e) {
            log.error("极光推送调用失败", e);
        } catch (APIRequestException e) {
            log.error("极光推送调用失败, code:{}, msg:{}", e.getErrorCode(), e.getErrorMessage());
        }
    }

    /**
     * 全设备平台推送(IOS, Android)
     */
    public static PushPayload buildAllPlatformPush(String msg, Map<String, String> extras) {
        Notification notification = Notification.newBuilder()
                // ios设备
                .addPlatformNotification(IosNotification.newBuilder()
                        .setAlert(msg)
                        .setSound("default")
                        .addExtras(extras)
                        .build()
                )
                // 安卓设备
                .addPlatformNotification(AndroidNotification.newBuilder()
                        .setAlert(msg)
                        .addExtras(extras)
                        .build()
                )
                .build();


        return PushPayload.newBuilder()
                .setPlatform(Platform.android_ios())
                .setAudience(Audience.all())
                // 设置通知消息体(手机通知栏会显示消息
                .setNotification(notification)
                .build();
    }

    /**
     * 别名推送
     */
    public static PushPayload buildAliasPush(String msg, String[] alias, Map<String, String> extras) {
        // 设置通知消息体(手机通知栏会显示消息)
        Notification notification = Notification.newBuilder()
                // ios设备
                .addPlatformNotification(
                        IosNotification.newBuilder()
                                .setAlert(msg)
                                .setSound("happy.caf")
                                .addExtras(extras)
                                .build()
                )
                // 安卓设备
                .addPlatformNotification(AndroidNotification.newBuilder()
                        .setAlert(msg)
                        .addExtras(extras)
                        .build()
                )
                .build();

        return PushPayload.newBuilder()
                // 设备类型 andorid, ios等
                .setPlatform(Platform.all())
                // 手机端在极光注册的别名
                .setAudience(Audience.alias(alias))
                .setNotification(notification)
                .build();
    }

    public boolean setAlias(String alias, String registrationId) {
        try {
            DefaultResult result = pushClient.updateDeviceTagAlias(registrationId, alias, Collections.emptySet(), Collections.emptySet());
            log.info("极光设置别名返回结果, result:{}", result);
            return true;
        } catch (APIConnectionException e) {
            log.error("极光设置别名调用失败", e);
        } catch (APIRequestException e) {
            log.error("极光设置别名调用失败, code:{}, msg:{}", e.getErrorCode(), e.getErrorMessage());
        }

        return false;
    }

    public boolean deleteByRegistrationId(String alias, String registrationId) {
        try {
            DefaultResult result = pushClient.removeDevicesFromAlias(alias, new HashSet<>(Arrays.asList(registrationId)));
            log.info("极光删除别名返回结果, result:{}", result);
            return true;
        } catch (APIConnectionException e) {
            log.error("极光删除别名调用失败", e);
        } catch (APIRequestException e) {
            log.error("极光删除别名调用失败, code:{}, msg:{}", e.getErrorCode(), e.getErrorMessage());
        }

        return false;
    }

    public int getMaxAliasBindDevice() {
        return MAX_ALIAS_BIND_DEVICE;
    }
}
